﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using EFDataModel.Enums;
using EFDebugging;

namespace EFDataModel
{
    /// <summary>
    /// Dynamische Datentypen, für jedes Modul können beliebig viele zusätzliche Eingabefelder festgelegt werden die angezeigt werden sollen
    /// </summary>
    public partial class EFDynamicDataType : IComparable
    {
        #region Static
        private static SortedList<int, EFDynamicDataType> _dynamicDataTypeSortedList;
        private static List<EFDynamicDataType> _dynamicDataTypeList;
        private static List<DynamicDataTypeListEntry> _dynamicDataTypeListEntries;
        /// <summary>
        /// Eine Statische Liste mit allen zur verfügung stehenden dynamischen Datentypen. Da sich die liste nur selten ändert wirde diese im Speicher gehalten als Statik.
        /// ACHTUNG es muss aufgepasst werden wo die Liste verwendet wird, da es sonst zu Problemen kommen kann mit den Werten die angezeigt werden, denn wes werden nicht alle 
        /// Properties gesetzt.
        /// </summary>
        /// <returns>SortedList(LFD, EFDynamicDataType)</returns>
        public static SortedList<int, EFDynamicDataType> GetDynamicDataTypes(EFModelContainer efModelContainer)
        {
            //Wenn eine der beiden Listen noch nicht existiert werden diese erstellt.
            if (_dynamicDataTypeList == null || _dynamicDataTypeSortedList == null)
            {
                //Alle DynamicDataItems heraussuchen und in einer lokalen Liste verwalten
                var allItems = from eDynamicDataType in efModelContainer.EFDynamicDataTypeMenge select eDynamicDataType;
                //Initialisierien der Listen
                _dynamicDataTypeSortedList = new SortedList<int, EFDynamicDataType>();
                _dynamicDataTypeList = new List<EFDynamicDataType>();
                _dynamicDataTypeListEntries = new List<DynamicDataTypeListEntry>();

                foreach (EFDynamicDataType eDynamicDataType in allItems)
                {
                    //Erstellen und füllen der lokalen liste für die Datentypen.
                    EFDynamicDataType newItem = new EFDynamicDataType();
                    newItem.CustomCssClass = eDynamicDataType.CustomCssClass;
                    newItem.Description = eDynamicDataType.Description;
                    newItem.DynamicDataTypeId = eDynamicDataType.DynamicDataTypeId;
                    newItem.FormatString = eDynamicDataType.FormatString;
                    newItem.ID = eDynamicDataType.ID;
                    newItem.IsRequired = eDynamicDataType.IsRequired;
                    newItem.IsSystem = eDynamicDataType.IsSystem;
                    newItem.IsVisible = eDynamicDataType.IsVisible;
                    newItem.ModuleId = eDynamicDataType.ModuleId;
                    newItem.Name = eDynamicDataType.Name;
                    newItem.RegExString = eDynamicDataType.RegExString;
                    newItem.SortId = eDynamicDataType.SortId;
                    newItem.MaxLength = eDynamicDataType.MaxLength;
                    newItem.MinLength = eDynamicDataType.MinLength;
                    //Die passende Kategorie heraussuchen die dem Datentyp zugeordnet ist.
                    newItem.DynamicDataTypeCategory = eDynamicDataType.DynamicDataTypeCategory;
                    newItem.DynamicDataTypeCategoryID = eDynamicDataType.DynamicDataTypeCategoryID;

                    //Es müssen auch die Gruppen für Lese und Schreibrechte herausgesucht werden und auch unserem Dynamischen
                    //Datentyp der als Statische Liste verwaltet wird hinzugefügt werden
                    if (eDynamicDataType.UserGroupsRead.Count() > 0)
                    {
                        foreach (UserGroup eUserGroupRead in eDynamicDataType.UserGroupsRead)
                        {
                            //Anlegen eines "neuen" Eintrages in unserer Statischen Liste, nicht einfach den Eintrag aus dem Foreach nehmen!
                            //Achtung, daher kann es passieren, das evtl. Werte nicht gesetzt sind, in unserem Entry, daher evtl. hier den benötigten Wert hinzufügen
                            newItem.UserGroupsRead.Add(new UserGroup()
                                                           {
                                                               ID = eUserGroupRead.ID,
                                                               Name = eUserGroupRead.Name,
                                                               SortId = eUserGroupRead.SortId,
                                                               IsSystem = eUserGroupRead.IsSystem,
                                                               ModuleId = eUserGroupRead.ModuleId,
                                                               CategoryTypeId = eUserGroupRead.CategoryTypeId,
                                                               TypeId = eUserGroupRead.TypeId
                                                           });
                        }
                    }

                    //Statische Liste für unsere Schreibrechte hinzufügen
                    if (eDynamicDataType.UserGroupsWrite.Count() > 0)
                    {
                        foreach (UserGroup eUserGroupWrite in eDynamicDataType.UserGroupsWrite)
                        {
                            //Anlegen eines "neuen" Eintrages in unserer Statischen Liste, nicht einfach den Eintrag aus dem Foreach nehmen!
                            //Achtung, daher kann es passieren, das evtl. Werte nicht gesetzt sind, in unserem Entry, daher evtl. hier den benötigten Wert hinzufügen
                            newItem.UserGroupsWrite.Add(new UserGroup()
                            {
                                ID = eUserGroupWrite.ID,
                                Name = eUserGroupWrite.Name,
                                SortId = eUserGroupWrite.SortId,
                                IsSystem = eUserGroupWrite.IsSystem,
                                ModuleId = eUserGroupWrite.ModuleId,
                                CategoryTypeId = eUserGroupWrite.CategoryTypeId,
                                TypeId = eUserGroupWrite.TypeId
                            });
                        }
                    }

                    //Es muss geprüft werden ob ListEntries enthalten sind am aktuellen Eintrag, dann müssen diese auch hinzugefügt werden
                    if (eDynamicDataType.DynamicDataTypeListEntries.Count() > 0)
                    {
                        //Erst in eine Liste laden, da sich sonst immer die Anzahl der Einträge geändert hat und foreach nicht funktionierte k.a. Warum!
                        List<DynamicDataTypeListEntry> entries = eDynamicDataType.DynamicDataTypeListEntries.ToList();
                        foreach (DynamicDataTypeListEntry eDynamicDataTypeListEntry in entries)
                        {
                            //Anlegen eines "neuen" Eintrages in unserer Statischen Liste, nicht einfach den Eintrag aus dem Foreach nehmen!
                            //Achtung, daher kann es passieren, das evtl. Werte nicht gesetzt sind, in unserem Entry, daher evtl. hier den benötigten Wert hinzufügen
                            DynamicDataTypeListEntry dataTypeListEntry = new DynamicDataTypeListEntry()
                                                                             {
                                                                                 ID = eDynamicDataTypeListEntry.ID,
                                                                                 Name = eDynamicDataTypeListEntry.Name,
                                                                                 SortId = eDynamicDataTypeListEntry.SortId,
                                                                                 EFDynamicDataType = eDynamicDataType,
                                                                                 EFDynamicDataTypeID = eDynamicDataType.ID
                                                                             };
                            
                            newItem.DynamicDataTypeListEntries.Add(dataTypeListEntry);
                            //Auch eine extra Liste in der nur die List Entries enthalten sind mit Daten füllen
                            _dynamicDataTypeListEntries.Add(dataTypeListEntry);
                        }
                    }

                    //Die SortedList sowie die normale Liste mit Daten füllen
                    _dynamicDataTypeSortedList.Add(eDynamicDataType.ID, newItem);
                    _dynamicDataTypeList.Add(newItem);
                }
            }

            return _dynamicDataTypeSortedList;
        }

        /// <summary>
        /// Nur die Liste mit den dynamischen Datentypen zurückgegeben
        /// </summary>
        /// <returns>Statische Liste mit den DynamicDataTypes, die im System hinterlegt sind</returns>
        public static List<EFDynamicDataType> GetDynamicDataTypesList(EFModelContainer efModelContainer)
        {
            if (_dynamicDataTypeList == null)
            {
                GetDynamicDataTypes(efModelContainer);
            }

            return _dynamicDataTypeList;
        }

        /// <summary>
        /// Gibt die Statische Liste mit allen im System zur Verfügung stehenden Dynamischen Listeneinträgen zurück
        /// </summary>
        /// <returns>Statische Liste mit allen im System zur Verfügung stehenden DynamischenListEntries</returns>
        public static List<DynamicDataTypeListEntry> GetDynamicDataTypeListEntries(EFModelContainer efModelContainer)
        {
            if (_dynamicDataTypeListEntries == null)
            {
                GetDynamicDataTypes(efModelContainer);
            }

            return _dynamicDataTypeListEntries;
        }

        /// <summary>
        /// Zurücksetzten der statischen Liste mit den Dynamischen Datetypen.
        /// </summary>
        public static void ResetDynamicDataTypeList()
        {
            _dynamicDataTypeList = null;
            _dynamicDataTypeSortedList = null;
        }

        /// <summary>
        /// Prefix der im Namen jedes Dynamischen Eingabefeldes vorkommt
        /// </summary>
        public const string DataTypeHtmlPrefix = "DynamicDataType";

        /// <summary>
        /// Gibt den passenden Namen für den übergebenen DynamicDataType zurück
        /// </summary>
        /// <param name="efDynamicDataType">Der ENum Wert zu dem der passende Name herausgesucht werden soll</param>
        /// <returns>String für den Enumwert</returns>
        public static string GetDynamicDataTypeName(EFDynamicDataTypes efDynamicDataType)
        {
            return Resources.DynamicDataTypeNames.ResourceManager.GetString(efDynamicDataType.ToString());
        }

        /// <summary>
        /// Ermittelt alle Eingabefelder die angezeigt werden sollen für eine übergenene DatensatzId
        /// </summary>
        /// <param name="baseId">DatensatzId für die die DataTypes rausgesucht werden sollen z.B. PersonId</param>
        /// <param name="viewType">Gibt an um welchen ViewType es sich handelt, "Details" oder "Edit"</param>
        /// <param name="efUser">Der User für den die Dynamischen Eingabefelder angezeigt werden sollen</param>
        /// <returns>Alle sichtbaren Dynamischen Datentypen für die übergenene BaseId</returns>
        public static IEnumerable<EFDynamicDataType> GetVisibleDataTypesByBaseId(int baseId, EFUser efUser, EFModelContainer efModelContainer, EViewType viewType)
        {
            //Erst den Basisdatensatz ermitteln um an die passende Modulid zu gelangen
            EFBase efBase = efModelContainer.EFBaseMenge.Find(baseId);
            if (efBase != null)
            {
                //Alle sichtbaren Eingabefelder für das Modul zurück geben welches die übergebene BaseId angehört
                return GetVisibleDataTypesByModulId(efBase.Module, efUser, efModelContainer, viewType);
            }

            //Eine leere Liste übergeben, wenn nicht gefunden werden konnte.
            return new List<EFDynamicDataType>();
        }

        /// <summary>
        /// Ermittelt alle Eingabefelder die angezeigt werden sollen für eine übergenenes Modul, anhand der ModulId und des Users welche Eingabefelder sichtbar sind.
        /// </summary>
        /// <param name="modul">Modul für das die DataTypes rausgesucht werden sollen z.B. Mitarbeiter</param>
        /// <param name="efUser">Der aktuelle User für den die Eingabefelder herrausgesucht werden sollen</param>
        /// <param name="viewType">Gibt an um welchen ViewType es sich handelt, "Details" oder "Edit"</param>
        /// <returns>Alle sichtbaren Dynamischen Datentypen für die übergenene BaseId</returns>
        public static IEnumerable<EFDynamicDataType> GetVisibleDataTypesByModulId(EFModules modul, EFUser efUser, EFModelContainer efModelContainer, EViewType viewType)
        {
            //Für die Detailsansicht müssen andere Rechte geprüft werden wie für die Edit Ansicht.
            if (viewType == EViewType.Details)
            {
                //Ermitteln aller Dynamischen Eingabefelder die in der Detailsansicht dargestellt werden dürfen, dabei werden all die Felder angezeigt, 
                //wo der User in der passenden Lese bzw. Schreibliste enthalten ist, bzw. wenn keine Rechte gesetzt wurden in beiden Listen und
                //die Felder sichtbar sind und der passenden ModulID entsprechen
                var fields = from eFields in GetDynamicDataTypesList(efModelContainer)
                             where eFields.ModuleId == (int)modul && eFields.IsVisible &&
                                   ((eFields.UserGroupsRead.Count == 0 && eFields.UserGroupsWrite.Count == 0) ||
                                 //WICHTIG Beim Überprüfen der UserGroup kann nicht Contains verwendet werden, sondern muss mit where gearbeitet werden,
                                 //da man nicht die Typen aus der DB mit unserem Statischen Eintrag vergleichen kann!
                                   (from eUserGroup in efUser.UserGroups where eFields.UserGroupsRead.Where(p => p.ID == eUserGroup.ID).Count() > 0 select eUserGroup.ID).Count() > 0 ||
                                   (from eUserGroup in efUser.UserGroups where eFields.UserGroupsWrite.Where(p => p.ID == eUserGroup.ID).Count() > 0 select eUserGroup.ID).Count() > 0)
                             select eFields;

                return fields;
            }

            //Für die Bearbeitenansicht, müssen bestimmte Rechte geprüft werden
            if (viewType == EViewType.Edit)
            {
                //Ermitteln aller sichbaren Dynamischen Eingabefelder, die sichtbar sind und der passenden ModulID entsprechen,
                //Außerdem muss noch überprüft werden ob der Eintrag in der Schreibliste des Users enthalten ist. Wenn garkeine Schreib oder
                //Leserechte gesetzt wurden, dann ist der Dynamische Datentyp für jeden zugänglich.
                var fields = from eFields in GetDynamicDataTypesList(efModelContainer)
                             where eFields.ModuleId == (int)modul && eFields.IsVisible &&
                                   ((eFields.UserGroupsRead.Count == 0 && eFields.UserGroupsWrite.Count == 0) ||
                                 //WICHTIG Beim Überprüfen der UserGroup kann nicht Contains verwendet werden, sondern muss mit where gearbeitet werden,
                                 //da man nicht die Typen aus der DB mit unserem Statischen Eintrag vergleichen kann!
                                   (from eUserGroup in efUser.UserGroups where eFields.UserGroupsWrite.Where(p => p.ID == eUserGroup.ID).Count() > 0 select eUserGroup).Count() > 0)
                             select eFields;
                return fields;
            }

            //Eine Leere Liste zuürckgeben, wenn ein EnumType nicht abgefangen wird.
            return new List<EFDynamicDataType>();
        }
        #endregion

        #region Member
        /// <summary>
        /// Den aktuellen DynamicDataType zurück gegeben für diesen Datentyp
        /// </summary>
        public EFDynamicDataTypes DynamicDataType
        {
            get { return (EFDynamicDataTypes)Enum.Parse(typeof(EFDynamicDataTypes), this.DynamicDataTypeId.ToString()); }
            set { DynamicDataTypeId = (int)value; }
        }

        /// <summary>
        /// Gibt zurück ob für den aktuellen DynamicDataType UserGruppen hinterlegt wurden.
        /// TRUE->Es wurden Rechte festgelegt | FALSE->Keine Rechte festgelegt
        /// </summary>
        public bool HasUserGroups
        {
            get
            {
                if (UserGroupsRead.Count == 0 && UserGroupsWrite.Count == 0)
                {
                    return false;
                }

                return true;
            }
        }

        /// <summary>
        /// Das zugeordnete Modul für den DynamicDataType
        /// </summary>
        public EFModules Module
        {
            get { return (EFModules)Enum.Parse(typeof(EFModules), ModuleId.ToString()); }
            set { ModuleId = (int)value; }
        }

        /// <summary>
        /// Gibt den String zurück der in der Oberfläche als Name im HTML-Element für diesen Datentypen genutzt werden soll.
        /// </summary>
        public string HtmlName
        {
            get { return string.Format("{0}_{1}_{2}", DataTypeHtmlPrefix, DynamicDataType.ToString(), ID); }
        }
        #endregion

        #region Public Functions
        /// <summary>
        /// Prüft ob sich die Werte für den übergebenen Datentyp geändert haben oder nicht. D.h. damit nicht alle leeren Strings gespeichert werden, 
        /// sondern nur die wo auch Werte hinterlegt sind bzw. waren.
        /// </summary>
        /// <param name="baseId">DatensatzId für die die DataTypes rausgesucht werden sollen z.B. PersonId</param>
        /// <param name="value">der wert der überprüft werden soll, ob sich dieser geändert hat.</param>
        /// <returns>Alle sichtbaren Dynamischen Datentypen für die übergenene BaseId</returns>
        public bool CheckValueChanged(int baseId, dynamic value)
        {
            //Kombinationen:
            //Vorher: String wurde gefunden -> Neuer Wert: Leerer string -> true (sonst können keine bereits existierenden Strings wieder gelöscht werden!)
            //Vorher: kein String gefunden -> Neuer Wert: Leerer string -> false
            //Vorher: kein String gefunden -> Neuer Wert: String enthalten -> true

            if (EFDynamicData.Count > 0)
            {
                //In der Liste für den aktuellen Dynamischen Datentyp prüfen ob für den übergebene BaseId und DatentypId ein Wert hinterlegt ist
                EFDynamicData efDynamicData = EFDynamicData.Where(p => p.EFDynamicDataTypeID == ID && p.EFBaseID == baseId).FirstOrDefault();
                //Es ist ein Wert hinterlegt, d.h. es muss auch etwas geändert werden, egal wie der neue String aussieht.
                if (efDynamicData != null)
                {
                    return true;
                }
            }

            //Es wurde ein Wert übergeben, dieser konnte aber nicht gefunden werden, d.h. er muss gespeichert werden (es wird ein neuer Eintrag angelegt)
            if (!string.IsNullOrEmpty(value))
            {
                return true;
            }

            //Eine leere Liste übergeben, wenn nicht gefunden werden konnte, dann wird auch nichts gespeichert
            return false;
        }

        /// <summary>
        /// Gibt den Namen des Datentyps für den aktuellen Dynamischen Datentyps zurück
        /// </summary>
        public string GetDynamicDataTypeName()
        {
            return GetDynamicDataTypeName(DynamicDataType);
        }

        /// <summary>
        /// Löschen des aktuellen DynamicDataTypes
        /// </summary>
        /// <returns>TRUE->Löschen erfolgreich | FALSE->Fehler beim Löschen</returns>
        public bool Delete(EFModelContainer efModelContainer)
        {
            //Kann gelöscht werden, da der Datentyp noch nicht verwendet wird.
            try
            {
                //Alle abhängigen Listeneinträge ebenfalls löschen.
                DynamicDataTypeListEntries.Clear();
                UserGroupsRead.Clear();
                UserGroupsWrite.Clear();

                efModelContainer.EFDynamicDataTypeMenge.Remove(this);
                efModelContainer.SaveChanges();
                ResetDynamicDataTypeList();
            }
            catch (Exception exception)
            {
                Debugging.WriteLog("Fehler beim Löschen eines Dynamischen Datentyps", exception);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Vergleicht die aktuelle Instanz mit einem anderen Objekt vom selben Typ und gibt eine ganze Zahl zurück, die angibt, ob die aktuelle Instanz in der Sortierreihenfolge vor oder nach dem anderen Objekt oder an derselben Position auftritt.
        /// </summary>
        /// <returns>
        /// Ein Wert, der die relative Reihenfolge der verglichenen Objekte angibt.Der Rückgabewert hat folgende Bedeutung:Wert Bedeutung Kleiner als 0 Diese Instanz ist kleiner als <paramref name="obj"/>. 0 Diese Instanz ist gleich <paramref name="obj"/>. Größer als 0 Diese Instanz ist größer als <paramref name="obj"/>. 
        /// </returns>
        /// <param name="obj">Ein Objekt, das mit dieser Instanz verglichen werden soll. </param><exception cref="T:System.ArgumentException"><paramref name="obj"/> hat nicht denselben Typ wie diese Instanz. </exception><filterpriority>2</filterpriority>
        public int CompareTo(object obj)
        {
            if (obj != null && obj.GetType() == typeof(EFDynamicDataType))
            {
                EFDynamicDataType efDynamicDataType = obj as EFDynamicDataType;
                return string.Compare(this.Name, efDynamicDataType.Name);
            }

            return 0;
        }
        #endregion
    }
}
